package com.dsit.hb950.file_service;

import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class FileSocketServer {
    public interface LoadingListener{
        void onLoadingProgress(File file,int transferedBytes,int totalLen);
        void onLoadingFinish(File file);
        void onLoadingError(Exception ex);
    }
    LoadingListener loadingListener;

    String TAG = "FileSocketServer";
	private String uploadPath="/storage/sdcard0";// 默认的保存位置
	private ExecutorService executorService;// 线程池
	private ServerSocket ss = null;
	private int port;// 监听端口
	private boolean quit;// 是否退出
	private Map<Long, FileLog> datas = new HashMap<Long, FileLog>();// 存放断点数据，最好改为数据库存放

	public FileSocketServer(String uploadPath, int port) {
		this.port = port;
        if(uploadPath!=null && !uploadPath.equals("")){
            this.uploadPath = uploadPath;
        }
		// 初始化线程池
		executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
				.availableProcessors() * 50);
        Log.i(TAG,"--------> File socket Server Started... uploadPath:"+uploadPath+" port:"+port);
	}

    public void setLoadingListener(LoadingListener listener){
        this.loadingListener = listener;
    }

	// 启动服务
	public void start() throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    ss = new ServerSocket(port);
                    while (!quit) {
                        Socket socket = ss.accept();// 接受客户端的请求
                        Log.i(TAG,"new file upload client conneted");
                        // 为支持多用户并发访问，采用线程池管理每一个用户的连接请求
                        executorService.execute(new SocketTask(socket));// 启动一个线程来处理请求
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
	}

	// 退出
	public void quit() {
		this.quit = true;
		try {
			ss.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws Exception {
		FileSocketServer server = new FileSocketServer("/cache",7878);
		server.start();
	}

	private class SocketTask implements Runnable {
		private Socket socket;

		public SocketTask(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				System.out.println("accepted connenction from "
						+ socket.getInetAddress() + " @ " + socket.getPort());
				PushbackInputStream inStream = new PushbackInputStream(
						socket.getInputStream());
				// 得到客户端发来的第一行协议数据：Content-Length=143253434;filename=xxx.3gp;sourceid=
				// 如果用户初次上传文件，sourceid的值为空。
				String head = StreamTool.readLine(inStream);
				Log.i(TAG,"===> FILE info:"+head);
				if (head != null) {
					// 下面从协议数据中读取各种参数值
					String[] items = head.split(";");
					String filelength = items[0].substring(items[0].indexOf("=") + 1);
					String filename = items[1].substring(items[1].indexOf("=") + 1);
					String sourceid = items[2].substring(items[2].indexOf("=") + 1);
					String md5 = items[3].substring(items[3].indexOf("=") + 1);// 文件校验值
					Long id = System.currentTimeMillis();
					File file = null;
					int position = 0;

                    File dir = new File(uploadPath);
                    if(!dir.exists())
                        dir.mkdirs();
                    file = new File(dir, filename);
                    if(file.exists()){
                        file.delete();
                        file = new File(dir, filename);
                    }

					RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
                    int fileToalLen = Integer.valueOf(filelength);
					if(position==0) fileOutStream.setLength(fileToalLen);//设置文件长度
					fileOutStream.seek(position);//移动文件指定的位置开始写入数据
					byte[] buffer = new byte[8*1024];
					int len = -1;
					int length = position;
					while( (len=inStream.read(buffer)) != -1){//从输入流中读取数据写入到文件中
						fileOutStream.write(buffer, 0, len);
						length += len;
                        if(loadingListener!=null){
                            loadingListener.onLoadingProgress(file,length,fileToalLen);// 当前传输的文件进度信息，回调通知
                        }
					}
					if(length==fileOutStream.length()) delete(id);
					fileOutStream.close();					
					inStream.close();

                    String md5sum = FileMD5Checker.calculateSingleFileMD5sum(file);
					Log.i(TAG,"md5_client="+md5);
					Log.i(TAG,"md5_server="+md5sum);
                    if(md5.equals(md5sum)){
                        Log.i(TAG,"========>上传的文件md5校验成功");
                        if(loadingListener!=null){
                            loadingListener.onLoadingFinish(file);
                        }
                    }else{
						Log.e(TAG,"========> 上传的文件md5校验失败");
                        if(loadingListener!=null){
                            loadingListener.onLoadingError(new Exception("md5 Error"));
                        }
					}
					file = null;
				}
			} catch (Exception e) {
				e.printStackTrace();
                if(loadingListener!=null){
                    loadingListener.onLoadingError(e);
                }
			} finally {
				try {
	                if(socket != null && !socket.isClosed()) socket.close();
	            } catch (IOException e) {}
			}
		}

	}

	public FileLog find(Long sourceid) {
		return datas.get(sourceid);
	}

	// 保存上传记录
	public void save(Long id, File saveFile) {
		// 日后可以改成通过数据库存放
		datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));
	}

	// 当文件上传完毕，删除记录
	public void delete(long sourceid) {
		if (datas.containsKey(sourceid))
			datas.remove(sourceid);
	}

	private class FileLog {
		private Long id;
		private String path;
		
		public FileLog(Long id, String path) {
			super();
			this.id = id;
			this.path = path;
		}

		public Long getId() {
			return id;
		}

		public void setId(Long id) {
			this.id = id;
		}

		public String getPath() {
			return path;
		}

		public void setPath(String path) {
			this.path = path;
		}

	}
}
